package utility

import (
	"context"
	"crypto/md5"
	"encoding/hex"
	"encoding/json"
	"fmt"
	"net/http"
	"os"

	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/net/ghttp"
	"github.com/gogf/gf/v2/text/gstr"
	"github.com/gogf/gf/v2/util/gconv"

	"github.com/bwmarrin/snowflake"
	"github.com/gogf/gf/v2/crypto/gmd5"
	"github.com/gogf/gf/v2/encoding/gurl"
	"github.com/gogf/gf/v2/errors/gerror"

	"github.com/gogf/gf/v2/os/glog"
	"github.com/gogf/gf/v2/os/gtime"

	"log"
	"math/rand"

	"net"
	"net/url"
	"regexp"
	"strconv"
	"strings"
	"time"
	"video-api/utility/redis_client"
)

var snowflakeGenerate *snowflake.Node

func newSnowflake() (sg *snowflake.Node, err error) {
	if snowflakeGenerate == nil {
		// 创建一个新的Node，设置节点ID（例如：机器ID和数据中心ID的组合）
		snowflakeGenerate, err = snowflake.NewNode(1)
		if err != nil {
			log.Fatalf("无法创建雪花算法节点: %v", err)
		}
	}
	return snowflakeGenerate, nil
}

func Param(r *http.Request, key string) string {
	var value string
	value = r.FormValue(key)
	if len(value) > 0 {
		return value
	}
	value = r.URL.Query().Get(key)
	if len(value) > 0 {
		return value
	}
	value = r.Header.Get(key)
	if len(value) > 0 {
		return value
	}
	if cookie, _ := r.Cookie(key); cookie != nil {
		return cookie.Value
	}
	return value
}

// GetSnowflakeId 获取雪花算法id
func GetSnowflakeId() int64 {
	//node, err := newSnowflake()
	//if err != nil {
	//	return 0
	//}
	//if node == nil {
	//	return 0
	//}
	//return node.Generate().Int64()
	return gtime.Timestamp()
}

// EncryptPassword 密码加密
func EncryptPassword(password, salt string) string {
	return gmd5.MustEncryptString(gmd5.MustEncryptString(password) + gmd5.MustEncryptString(salt))
}

// TimeStampToDateTime 时间戳转 yyyy-MM-dd HH:mm:ss
func TimeStampToDateTime(timeStamp int64) string {
	tm := gtime.NewFromTimeStamp(timeStamp)
	return tm.Format("Y-m-d H:i:s")
}

// TimeStampToDate 时间戳转 yyyy-MM-dd
func TimeStampToDate(timeStamp int64) string {
	tm := gtime.NewFromTimeStamp(timeStamp)
	return tm.Format("Y-m-d")
}

// GetDomain 获取当前请求接口域名
func GetDomain(r *ghttp.Request) (string, error) {
	pathInfo, err := gurl.ParseURL(r.GetUrl(), -1)
	if err != nil {
		g.Log().Error(context.TODO(), err)
		err = gerror.New("解析附件路径失败")
		return "", err
	}
	return fmt.Sprintf("%s://%s:%s/", pathInfo["scheme"], pathInfo["host"], pathInfo["port"]), nil
}

// GetUserAgent 获取user-agent
func GetUserAgent(ctx context.Context) string {
	return ghttp.RequestFromCtx(ctx).Header.Get("User-Agent")
}

// GetClientIp 获取客户端IP
func GetClientIp(r *ghttp.Request) string {
	if r == nil {
		return ""
	}
	ip := r.Header.Get("X-Forwarded-For")
	if ip == "" {
		ip = r.GetClientIp()
	}
	return ip
}

// GetLocalIP 服务端ip
func GetLocalIP() (ip string, err error) {
	addrs, err := net.InterfaceAddrs()
	if err != nil {
		return
	}
	for _, addr := range addrs {
		ipAddr, ok := addr.(*net.IPNet)
		if !ok {
			continue
		}
		if ipAddr.IP.IsLoopback() {
			continue
		}
		if !ipAddr.IP.IsGlobalUnicast() {
			continue
		}
		return ipAddr.IP.String(), nil
	}
	return
}

// GetCityByIp 获取ip所属城市
//func GetCityByIp(ip string) string {
//	if ip == "" {
//		return ""
//	}
//	if ip == "[::1]" || ip == "127.0.0.1" {
//		return "内网IP"
//	}
//	url := "http://whois.pconline.com.cn/ipJson.jsp?json=true&ip=" + ip
//	bytes := g.Client().GetBytes(context.TODO(), url)
//	src := string(bytes)
//	srcCharset := "GBK"
//	tmp, _ := gcharset.ToUTF8(srcCharset, src)
//	json, err := gjson.DecodeToJson(tmp)
//	if err != nil {
//		return ""
//	}
//	if json.GetInt("code") == 0 {
//		city := fmt.Sprintf("%s %s", json.GetString("pro"), json.GetString("city"))
//		return city
//	} else {
//		return ""
//	}
//}

// GetCountryByIp 根据ip获取国家
func GetCountryByIp(ip string) string {
	if ip == "" {
		return ""
	}
	if ip == "[::1]" || ip == "::1" || ip == "127.0.0.1" {
		return "127.0.0.1"
	}
	type IpInfo struct {
		City    string `json:"city"`
		Region  string `json:"region"`
		Country string `json:"country"`
	}
	countryUrl := fmt.Sprintf("https://ipinfo.io/%s/json", ip)
	bytes := g.Client().GetBytes(context.TODO(), countryUrl)
	ipInfo := &IpInfo{}
	err := json.Unmarshal(bytes, ipInfo)
	if err != nil {
		return ""
	}
	return ipInfo.Country
}

// StrToTimestamp 日期字符串转时间戳（秒）
func StrToTimestamp(dateStr string) int64 {
	tm, err := gtime.StrToTime(dateStr)
	if err != nil {
		g.Log().Error(context.TODO(), err)
		return 0
	}
	return tm.Timestamp()
}

// GetDbConfig get db config
//func GetDbConfig() (cfg *gdb.ConfigNode, err error) {
//	cfg = g.DB().GetConfig()
//	err = ParseDSN(cfg)
//	return
//}
//
//// 日期字符串转时间戳（秒）
//func StrToTimestamp(dateStr string) int64 {
//	tm, err := gtime.StrToTime(dateStr)
//	if err != nil {
//		g.Log().Error(err)
//		return 0
//	}
//	return tm.Timestamp()
//}
//
//// GetDbConfig get db config
////func GetDbConfig() (cfg *gdb.ConfigNode, err error) {
////	cfg = g.DB().GetConfig()
////	err = ParseDSN(cfg)
////	return
////}
//
//// ParseDSN parses the DSN string to a Config
////func ParseDSN(cfg *gdb.ConfigNode) (err error) {
////	defer func() {
////		if r := recover(); r != nil {
////			err = gerror.New(r.(string))
////		}
////	}()
////	dsn := cfg.Link
////	if dsn == "" {
////		return
////	}
////	foundSlash := false
////	// gfast:123456@tcp(192.168.0.212:3306)/gfast-v2
////	for i := len(dsn) - 1; i >= 0; i-- {
////		if dsn[i] == '/' {
////			foundSlash = true
////			var j, k int
////
////			// left part is empty if i <= 0
////			if i > 0 {
////				// [username[:password]@][protocol[(address)]]
////				// Find the last '@' in dsn[:i]
////				for j = i; j >= 0; j-- {
////					if dsn[j] == '@' {
////						// username[:password]
////						// Find the first ':' in dsn[:j]
////						for k = 0; k < j; k++ {
////							if dsn[k] == ':' {
////								cfg.Pass = dsn[k+1 : j]
////								cfg.User = dsn[:k]
////								break
////							}
////						}
////						break
////					}
////				}
////
////				// gfast:123456@tcp(192.168.0.212:3306)/gfast-v2
////				// [protocol[(address)]]
////				// Find the first '(' in dsn[j+1:i]
////				var h int
////				for k = j + 1; k < i; k++ {
////					if dsn[k] == '(' {
////						// dsn[i-1] must be == ')' if an address is specified
////						if dsn[i-1] != ')' {
////							if strings.ContainsRune(dsn[k+1:i], ')') {
////								panic("invalid DSN: did you forget to escape a param value?")
////							}
////							panic("invalid DSN: network address not terminated (missing closing brace)")
////						}
////						for h = k + 1; h < i-1; h++ {
////							if dsn[h] == ':' {
////								cfg.Host = dsn[k+1 : h]
////								cfg.Port = dsn[h+1 : i-1]
////								break
////							}
////						}
////						break
////					}
////				}
////			}
////			for j = i + 1; j < len(dsn); j++ {
////				if dsn[j] == '?' {
////					cfg.Name = dsn[i+1 : j]
////					break
////				} else {
////					cfg.Name = dsn[i+1:]
////				}
////			}
////			break
////		}
////	}
////	if !foundSlash && len(dsn) > 0 {
////		panic("invalid DSN: missing the slash separating the database name")
////	}
////	return
////}
//
//// 获取附件真实路径
//func GetRealFilesUrl(r *ghttp.Request, path string) (realPath string, err error) {
//	if gstr.ContainsI(path, "http") {
//		realPath = path
//		return
//	}
//	realPath, err = GetDomain(r)
//	if err != nil {
//		return
//	}
//	realPath = realPath + path
//	return
//}
//
//// 获取附件相对路径

// GetRealFilesUrl 获取附件真实路径
func GetRealFilesUrl(r *ghttp.Request, path string) (realPath string, err error) {
	if gstr.ContainsI(path, "http") {
		realPath = path
		return
	}
	realPath, err = GetDomain(r)
	if err != nil {
		return
	}
	realPath = realPath + path
	return
}

// GetFilesPath 获取附件相对路径
//func GetFilesPath(fileUrl string) (path string, err error) {
//	upType := gstr.ToLower(g.Cfg().GetString("upload.type"))
//	upPath := gstr.Trim(g.Cfg().GetString("upload.local.UpPath"), "/")
//	if upType != "local" || (upType == "local" && !gstr.ContainsI(fileUrl, upPath)) {
//		path = fileUrl
//		return
//	}
//	pathInfo, err := gurl.ParseURL(fileUrl, 32)
//	if err != nil {
//		g.Log().Error(err)
//		err = gerror.New("解析附件路径失败")
//		return
//	}
//	pos := gstr.PosI(pathInfo["path"], upPath)
//	if pos >= 0 {
//		path = gstr.SubStr(pathInfo["path"], pos)
//	}
//	return
//}

//
//// 货币转化为分
//func CurrencyLong(currency interface{}) int64 {
//	strArr := gstr.Split(gconv.String(currency), ".")
//	switch len(strArr) {
//	case 1:
//		return gconv.Int64(strArr[0]) * 100
//	case 2:
//		if len(strArr[1]) == 1 {
//			strArr[1] += "0"
//		} else if len(strArr[1]) > 2 {
//			strArr[1] = gstr.SubStr(strArr[1], 0, 2)
//		}
//		return gconv.Int64(strArr[0])*100 + gconv.Int64(strArr[1])
//	}
//	return 0
//}
//
//func GetExcPath() string {
//	file, _ := exec.LookPath(os.Args[0])
//	// 获取包含可执行文件名称的路径
//	path, _ := filepath.Abs(file)
//	// 获取可执行文件所在目录
//	index := strings.LastIndex(path, string(os.PathSeparator))
//	ret := path[:index]
//	return strings.Replace(ret, "\\", "/", -1)
//}
//
//// 流水号
//func CreateLogSn(prefix string) string {
//	rand.Seed(time.Now().UnixNano())
//	return prefix + strings.Replace(time.Now().Format("20060102150405.000"), ".", "", -1) + strconv.Itoa(rand.Intn(899)+100)
//}
//
//// 获取随机整数
//func RandInt(max int) int {
//	rand.Seed(time.Now().UnixNano())
//	return rand.Intn(max)
//}

// CurrencyLong 货币转化为分
func CurrencyLong(currency interface{}) int64 {
	strArr := gstr.Split(gconv.String(currency), ".")
	switch len(strArr) {
	case 1:
		return gconv.Int64(strArr[0]) * 100
	case 2:
		if len(strArr[1]) == 1 {
			strArr[1] += "0"
		} else if len(strArr[1]) > 2 {
			strArr[1] = gstr.SubStr(strArr[1], 0, 2)
		}
		return gconv.Int64(strArr[0])*100 + gconv.Int64(strArr[1])
	}
	return 0
}

//func GetExcPath() string {
//	file, _ := exec.LookPath(os.Args[0])
//	// 获取包含可执行文件名称的路径
//	path, _ := filepath.Abs(file)
//	// 获取可执行文件所在目录
//	index := strings.LastIndex(path, string(os.PathSeparator))
//	ret := path[:index]
//	return strings.Replace(ret, "\\", "/", -1)
//}

// CreateLogSn 流水号
func CreateLogSn(prefix string) string {
	rand.Seed(time.Now().UnixNano())
	return prefix + strings.Replace(time.Now().Format("20060102150405.000"), ".", "", -1) + strconv.Itoa(rand.Intn(899)+100)
}

// RandInt 获取随机整数
func RandInt(max int) int {
	rand.Seed(time.Now().UnixNano())
	return rand.Intn(max)
}

// InSlice 元素是否存在于切片中
func InSlice[K comparable](slice []K, key K) bool {
	for _, v := range slice {
		if v == key {
			return true
		}
	}
	return false
}

// DeDuplicateSlice 数组去重
func DeDuplicateSlice[T any](array []T) []T {
	mp := make(map[any]struct{})
	idx := 0
	for _, value := range array {
		if _, ok := mp[value]; ok {
			continue
		}
		array[idx] = value
		idx = idx + 1
		mp[value] = struct{}{}
	}
	return array[:idx]
}

// ConvertToFloat64 将 score 转换为 float64 类型的辅助函数
func ConvertToFloat64(score interface{}) (float64, error) {
	switch v := score.(type) {
	case int, int8, int16, int32, int64:
		return float64(v.(int64)), nil
	case float32, float64:
		return float64(v.(float64)), nil
	default:
		return 0, fmt.Errorf("invalid score type: %T", v)
	}
}

// ConvertToString 将 value 转换为 string 类型的辅助函数
func ConvertToString(value interface{}) (string, error) {
	switch v := value.(type) {
	case string:
		return v, nil
	case []byte:
		return string(v), nil
	default:
		return "", fmt.Errorf("invalid value type: %T, expected string or []byte", v)
	}
}

// IsRequestAllowed 幂等性保护
func IsRequestAllowed(ctx context.Context, userId int, req any) (bool, error) {
	r := g.RequestFromCtx(ctx)
	params, err := json.Marshal(req)
	if err != nil {
		return false, err
	}
	if r != nil {
		path := r.Request.URL.Path
		hashInput := strconv.Itoa(userId) + path + string(params)
		hasher := md5.New()
		hasher.Write([]byte(hashInput))
		hashStr := hex.EncodeToString(hasher.Sum(nil))
		redisKey := "request:" + hashStr
		nx := redis_client.RedisClient.SetNX(ctx, redisKey, 1, 2*time.Second)
		if nx.Err() != nil {
			return false, nx.Err()
		}
		if nx.Val() == true {
			return true, nx.Err()
		}
		return false, nil
	}
	return false, nil
}

// InSliceExistStr 判断字符或切片字符是否存在指定字符
func InSliceExistStr(elems any, search string) bool {
	switch elems.(type) {
	case []string:
		elem := gconv.Strings(elems)
		for i := 0; i < len(elem); i++ {
			if gconv.String(elem[i]) == search {
				return true
			}
		}
	default:
		return gconv.String(elems) == search
	}
	return false
}

// IsDNSName 是否是域名地址
func IsDNSName(s string) bool {
	DNSName := `^([a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62}){1}(\.[a-zA-Z0-9_]{1}[a-zA-Z0-9_-]{0,62})*[\._]?$`
	rxDNSName := regexp.MustCompile(DNSName)
	return s != "" && rxDNSName.MatchString(s)
}

// IsHTTPS 是否是https请求
func IsHTTPS(ctx context.Context) bool {
	r := ghttp.RequestFromCtx(ctx)
	if r == nil {
		g.Log().Info(ctx, "IsHTTPS ctx not request")
		return false
	}
	return r.TLS != nil || gstr.Equal(r.Header.Get("X-Forwarded-Proto"), "https")
}

// IsIp 是否为ipv4
func IsIp(ip string) bool {
	return net.ParseIP(ip) != nil
}

// IsPublicIp 是否是公网IP
func IsPublicIp(ip string) bool {
	i := net.ParseIP(ip)
	if i.IsLoopback() || i.IsPrivate() || i.IsMulticast() || i.IsUnspecified() || i.IsLinkLocalUnicast() || i.IsLinkLocalMulticast() {
		return false
	}
	if ip4 := i.To4(); ip4 != nil {
		return !i.Equal(net.IPv4bcast)
	}
	return true
}

// IsLocalIPAddr 检测 IP 地址字符串是否是内网地址
func IsLocalIPAddr(ip string) bool {
	if "localhost" == ip {
		return true
	}
	return HasLocalIP(net.ParseIP(ip))
}

// HasLocalIP 检测 IP 地址是否是内网地址
func HasLocalIP(ip net.IP) bool {
	if ip.IsLoopback() {
		return true
	}
	ip4 := ip.To4()
	if ip4 == nil {
		return false
	}
	return ip4[0] == 10 || // 10.0.0.0/8
		(ip4[0] == 172 && ip4[1] >= 16 && ip4[1] <= 31) || // 172.16.0.0/12
		(ip4[0] == 169 && ip4[1] == 254) || // 169.254.0.0/16
		(ip4[0] == 192 && ip4[1] == 168) // 192.168.0.0/16
}

// IsMobile 是否为手机号码
func IsMobile(mobile string) bool {
	pattern := `^(1[2|3|4|5|6|7|8|9][0-9]\d{4,8})$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(mobile)
}

// IsEmail 是否为邮箱地址
func IsEmail(email string) bool {
	// pattern := `^[0-9a-z][_.0-9a-z-]{0,31}@([0-9a-z][0-9a-z-]{0,30}[0-9a-z].){1,4}[a-z]{2,4}$` //匹配电子邮箱
	pattern := `^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$`
	reg := regexp.MustCompile(pattern)
	return reg.MatchString(email)
}

// IsURL 是否是url地址
func IsURL(u string) bool {
	_, err := url.ParseRequestURI(u)
	if err != nil {
		return false
	}
	URL, err := url.Parse(u)
	if err != nil || URL.Scheme == "" || URL.Host == "" {
		return false
	}
	return true
}

// IsIDCard 是否为身份证
func IsIDCard(idCard string) bool {
	sz := len(idCard)
	if sz != 18 {
		return false
	}
	weight := []int{7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2}
	validate := []byte{'1', '0', 'X', '9', '8', '7', '6', '5', '4', '3', '2'}
	sum := 0
	for i := 0; i < len(weight); i++ {
		sum += weight[i] * int(byte(idCard[i])-'0')
	}
	m := sum % 11
	return validate[m] == idCard[sz-1]
}

// IsSameDay 是否为同一天
func IsSameDay(t1, t2 int64) bool {
	y1, m1, d1 := time.Unix(t1, 0).Date()
	y2, m2, d2 := time.Unix(t2, 0).Date()
	return y1 == y2 && m1 == m2 && d1 == d2
}

// IsSameMinute 是否为同一分钟
func IsSameMinute(t1, t2 int64) bool {
	d1 := time.Unix(t1, 0).Format("2006-01-02 15:04")
	d2 := time.Unix(t2, 0).Format("2006-01-02 15:04")
	return d1 == d2
}

// IsMobileVisit 是否为移动端访问
func IsMobileVisit(userAgent string) bool {
	if len(userAgent) == 0 {
		return false
	}
	is := false
	mobileKeywords := []string{"Mobile", "Android", "Silk/", "Kindle", "BlackBerry", "Opera Mini", "Opera Mobi"}
	for i := 0; i < len(mobileKeywords); i++ {
		if strings.Contains(userAgent, mobileKeywords[i]) {
			is = true
			break
		}
	}
	return is
}

// UniqueSlice 切片去重
func UniqueSlice[K comparable](languages []K) []K {
	result := make([]K, 0, len(languages))
	temp := map[K]struct{}{}
	for _, item := range languages {
		if _, ok := temp[item]; !ok {
			temp[item] = struct{}{}
			result = append(result, item)
		}
	}
	return result
}

func Remove(sl []interface{}, f func(v1 interface{}) bool) []interface{} {
	for k, v := range sl {
		if f(v) {
			sl[k] = sl[len(sl)-1]
			sl = sl[:len(sl)-1]
			return sl
		}
	}
	return sl
}

func RemoveSlice[K comparable](src []K, sub K) []K {
	for k, v := range src {
		if v == sub {
			copy(src[k:], src[k+1:])
			return src[:len(src)-1]
		}
	}
	return src
}

// DifferenceSlice 比较两个切片，返回他们的差集
// slice1 := []int{1, 2, 3, 4, 5}
// slice2 := []int{4, 5, 6, 7, 8}
// fmt.Println(Difference(slice1, slice2)) // Output: [1 2 3]
func DifferenceSlice[T comparable](s1, s2 []T) []T {
	m := make(map[T]bool)
	for _, item := range s1 {
		m[item] = true
	}
	var diff []T
	for _, item := range s2 {
		if _, ok := m[item]; !ok {
			diff = append(diff, item)
		}
	}
	return diff
}

// WriteJSONToFile 将 JSON 字符串写入文件
func WriteJSONToFile(filename string, jsonStr string) error {
	// 格式化 JSON 字符串
	var jsonObj interface{}
	if err := json.Unmarshal([]byte(jsonStr), &jsonObj); err != nil {
		return fmt.Errorf("failed to unmarshal JSON: %v", err)
	}

	prettyJSON, err := json.MarshalIndent(jsonObj, "", "    ")
	if err != nil {
		return fmt.Errorf("failed to marshal pretty JSON: %v", err)
	}

	// 写入文件
	file, err := os.Create(filename)
	if err != nil {
		return fmt.Errorf("failed to create file: %v", err)
	}
	defer file.Close()

	_, err = file.Write(prettyJSON)
	if err != nil {
		return fmt.Errorf("failed to write to file: %v", err)
	}

	return nil
}

func SafeGo(ctx context.Context, f func(ctx context.Context), lv ...int) {
	g.Go(ctx, f, func(ctx context.Context, err error) {
		var level = glog.LEVEL_ERRO
		if len(lv) > 0 {
			level = lv[0]
		}
		Logf(level, ctx, "SafeGo exec failed:%+v", err)
	})
}

func Logf(level int, ctx context.Context, format string, v ...interface{}) {
	switch level {
	case glog.LEVEL_DEBU:
		g.Log().Debugf(ctx, format, v...)
	case glog.LEVEL_INFO:
		g.Log().Infof(ctx, format, v...)
	case glog.LEVEL_NOTI:
		g.Log().Noticef(ctx, format, v...)
	case glog.LEVEL_WARN:
		g.Log().Warningf(ctx, format, v...)
	case glog.LEVEL_ERRO:
		g.Log().Errorf(ctx, format, v...)
	case glog.LEVEL_CRIT:
		g.Log().Criticalf(ctx, format, v...)
	case glog.LEVEL_PANI:
		g.Log().Panicf(ctx, format, v...)
	case glog.LEVEL_FATA:
		g.Log().Fatalf(ctx, format, v...)
	default:
		g.Log().Errorf(ctx, format, v...)
	}
}
